﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
using System.Xml;
using System.Reflection;

namespace Ecopsy.DataTier
{
    public class EntityList: Entity, IEnumerator, IEnumerable 
    {
        public EntityList():base() 
        {

            if (ImSupprotType(this.GetType().GetInterfaces(), typeof(ISqlSignature)))
                _RW_Engine = new SQL_RW_ListEng(this);
            else
                _RW_Engine = new RW_ListEng(this);            

        }
                
        private List<EntitySingle> _List    = new List<EntitySingle>();        
        private int _Index                  = -1;

        #region Tree
        public event AddChild OnAddChild;

        #endregion

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return this; 
        }

        #endregion

        #region IEnumerator Members

        [EntityNonSerialized]
        public object Current
        {
            get 
            {
                try
                {       
                    return  _List[_Index];                    
                    
                }
                catch 
                {
                    throw;
                }
            } 
        }      

        public bool MoveNext()
        {
            try
            {
                if (0 == _List.Count) return false;
                if (_Index > _List.Count || _Index == (_List.Count)) Reset();
                _Index++;
                bool r= (_List.Count != _Index);                
                return r;
            }
            catch
            {
                return false;
            }
        }

        public void Reset()
        {
            _Index = -1;
        }

        public virtual  void Add(object  obj)
        {
            _List.Add((EntitySingle) obj);         
        }

        public virtual void Add(ICollection  obj)
        {
            foreach (EntitySingle i in obj)
            {
                _List.Add(i);
            }
            
        }
               
        public void Remove(EntitySingle obj)
        {
            _List.Remove(obj);
        }
      
        public void Remove(string  ID)
        {
            EntitySingle f = _List.Find(delegate(EntitySingle Entity)
                {
                    return Entity.ID.Equals(
                        ID, StringComparison.OrdinalIgnoreCase);
                });   
            if(null!=f) _List.Remove(f);
        }

        [EntityNonSerialized]
        public virtual  EntitySingle this[object KeyValue]
        {
            get
            {
                int r = _Index;
                try
                {
                    Reset();
                    foreach (EntitySingle item in this) if (KeyValue.ToString()  == item.ID) return item;
                    return null;
                }

                catch
                {
                    throw;
                }
                finally
                {
                    _Index = r;
                }

            }
        }

        [EntityNonSerialized]
        public int Count { get { return _List.Count; } }

        public void Clear()
        {
            _List.Clear(); 
        }

        public EntitySingle GetFirst()
        {
            Reset();
            foreach (EntitySingle i in this) return i;
            return null;
        }

        #endregion

        #region Override
        
        public override void WriteXml(System.Xml.XmlWriter writer, string ObjectName, bool Full)
        {
            if (0 == Count) return;

            // для Sql списков - тока короткие деревья
            if (ImSql) 
            {
                WriteShortXml(writer, ObjectName);
                return;
            }

            Type MyType = this.GetType();

            writer.WriteStartElement(ObjectName);
            //// Обязательно пишем тип и сборку 
            writer.WriteAttributeString(SerilizeContext.TypePref, MyType.ToString());
            writer.WriteAttributeString(SerilizeContext.AssemblyPref, MyType.Assembly.ToString());
                        

            // пишем примитивные типы 
            foreach (PropertyInfo inf in AttPropertys)
            {
                object v = inf.GetValue(this, null);
                if (null == v) continue;
                if (String.IsNullOrEmpty(v.ToString())) continue;
                writer.WriteAttributeString(inf.Name, v.ToString());
            }   


            this.Reset();
            foreach (EntitySingle item in this)
            {
                // если объект удален нефиг его серилизовать
                if (item.State == EntityState.Deleted) continue;
  
                if (item.ImSql && !Full) // если объект поддерживает ImSql и это режим короткой записи - пишем короткий xml 
                {
                    ((ISerializableItem)item).WriteShortXml(writer, item.GetType().Name );
                    continue;
                }
                ((ISerializableItem)item).WriteXml(writer, item.GetType().Name, Full);
            }

            // конец узла
            writer.WriteEndElement();
        }

        /// <summary>
        /// Краткая запись объектов списка в формате 
        /// 
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="ObjectName"></param>
        public override void WriteShortXml(XmlWriter writer, string ObjectName)
        {
            if (0 == Count) return;

            Type MyType = this.GetType();

            writer.WriteStartElement(ObjectName);
            //// Обязательно пишем тип и сборку 
            writer.WriteAttributeString(SerilizeContext.TypePref, MyType.ToString());
            writer.WriteAttributeString(SerilizeContext.AssemblyPref, MyType.Assembly.ToString());

            Type t = MyGetType(MyType.Assembly.ToString(), MyType.ToString());

            foreach (PropertyInfo inf in AttPropertys)
            {
                object v = inf.GetValue(this, null);
                if (null == v) continue;
                if (String.IsNullOrEmpty(v.ToString())) continue;
                writer.WriteAttributeString(inf.Name, v.ToString());
            }                


            this.Reset();
            foreach (EntitySingle item in this)
            {
                if(item.ImSql)  
                    ((ISerializableItem)item).WriteShortXml(writer, "Item");
                else
                    ((ISerializableItem)item).WriteXml (writer, "Item",true);
            }

            // конец узла
            writer.WriteEndElement();
            
        }

        public override void ReadXml(System.Xml.XmlNode Node)
        {
            base.ReadXml(Node); 
            foreach (System.Xml.XmlNode nd in Node.ChildNodes)
            {
                string sAssmblyName = (ItemAssemblyName.Length == 0 ? nd.Attributes[SerilizeContext.AssemblyPref].Value : ItemAssemblyName);
                string sItemAssemblyType = (ItemAssemblyType.Length == 0 ? nd.Attributes[SerilizeContext.TypePref].Value : ItemAssemblyType);
                
                // это кэшируемый объект?
                XmlAttribute IsCacheObject = nd.Attributes["IsCacheObject"];
                if (null!=IsCacheObject)
                { 
                    string ID=nd.Attributes["ID"].Value;
                    if (CacheTools.IsCacheObject(ID))
                    { 
                        this.Add (CacheTools.GetCacheObect (ID));
                        continue ;
                    }


                }

                

                Type Tr = ReflectionItem.MyGetType(sAssmblyName, sItemAssemblyType);
                ISerializableItem obj = (ISerializableItem)Activator.CreateInstance(Tr);
                if (!(obj is EntityList))
                {
                    obj.ReadXml(nd);
                    Add(obj as EntitySingle);
                }
            }
            
        }

        public override void Save()
        {
            _RW_Engine.Save(); 
          
        }

        public override void Load()
        {
            _RW_Engine.Load();            
        }
        
        #endregion
       
       
        [EntityNonSerialized]
        public virtual string ListXML
        {
            get
            {
                if (0 == Count) return string.Empty;
                StringBuilder sb;
                MemoryStream ms = new MemoryStream();
                sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
                XmlTextWriter w = new XmlTextWriter(sw);
                w.Formatting = Formatting.Indented;
                w.Indentation = 3;

                Reset();
                foreach (EntitySingle item in this)
                {
                    w.WriteStartElement("Item");
                    w.WriteAttributeString("ID", item.ID);
                    w.WriteEndElement();
                }

                return sb.ToString();

            }
        }

        public List<EntitySingle> ToList()
        {   
            return ToList<EntitySingle>() ;
        }

        protected  class EntitySingleHasComparer : IEqualityComparer<EntitySingle>
        {
            public bool Equals(EntitySingle x, EntitySingle y)
            {
                return x.ID.Equals(y.ID);
            }

            public int GetHashCode(EntitySingle  obj)
            {

                Func<string, Int32> f = delegate(string s)
                {
                    Int32 res = 0;
                    foreach (char i in s.ToCharArray())
                    {
                        res = res + (int)i;

                    }
                    return res;
                };
                EntitySingle v = (EntitySingle)obj;
                return f(v.ID) ;
            }
        }

        public virtual  HashSet<EntitySingle> ToHashSet()
        {
            EntitySingleHasComparer cmp = new EntitySingleHasComparer();
            HashSet<EntitySingle> res = new HashSet<EntitySingle>(cmp);            
            this.Reset();
            foreach (EntitySingle item in this)
            {
                res.Add(item);
            }
            return res;



        }
        
        public  virtual HashSet<T> ToHashSet<T>(IEqualityComparer<T> cmp )
        {
            HashSet<T> res = new HashSet<T>(cmp);
            this.Reset();
            foreach (T item in this)
            {
                res.Add(item);
            }
            return res;

            

        }

        public List<T> ToList<T>()
        {
            List<T> r = new List<T>();
            Reset();
            foreach (T item in this)
            {     
                r.Add(item);
            }            
            return r;
        }

        public   T[] ToArray<T>()
        {
            T[] r = new T[this.Count];
            int i = 0;
            Reset();
            foreach (T item in this)
            {
                r[i]=item;
                i++;
            }
            return r;
        }

        protected class HasComparer : IEqualityComparer<EntitySingle>
        {
            public bool Equals(EntitySingle x, EntitySingle y)
            {
                if (x.ID.Equals(y.ID) )
                    return true;
                else
                    return false;
            }

            public int GetHashCode(EntitySingle obj)
            {

                Func<string, Int32> f = delegate(string s)
                {
                    Int32 res = 0;
                    foreach (char i in s.ToCharArray())
                    {
                        res = res + (int)i;

                    }
                    return res;
                };
                EntitySingle v = (EntitySingle)obj;
                return f(v.ID );
            }
            
        }


        

        //public virtual HashSet<T> ToHashSet(IEqualityComparer<T> HashSetComparer)
        //{
        //    HashSet<T> res = new HashSet<T>();
        //    this.Reset();
        //    foreach (EntitySingle i in this)
        //    {
        //        res.Add(i);
        //    }
        //    return res;
            
        //}

        /// <summary>
        /// Наименование тэга - под которым будут серилизоваться элементы списка - если не заданно 
        /// </summary>
        public virtual  string ListCaption
        {
            get { return this.GetType ().Name; }
            
        }
        
        /// <summary>
        /// Если указан тип - элементы списка будут инстанцироваться c заданной сборкой. Сборка в XML - игноируется.
        /// </summary>
        public virtual string ItemAssemblyName
        {
            get { return string.Empty; }  
        }
        
        /// <summary>
        /// Если указан тип - элементы списка будут инстанцироваться заданным типом. Тип указанный в XML - игноируется.
        /// </summary>
        public virtual string ItemAssemblyType
        {
            get { return string.Empty; }
        }

        public override void WriteReportXml(XmlWriter writer, string ObjectName)
        {
            foreach (EntitySingle item in this)
            {
                item.WriteReportXml(writer, string.Empty);
            }
        }
       
    }    
   }

